package com.yeskery.nut.scan.advice;

import com.yeskery.nut.annotation.web.*;
import com.yeskery.nut.plugin.PluginManager;
import com.yeskery.nut.scan.AnnotationHandler;
import com.yeskery.nut.scan.BeanAnnotationScanMetadata;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 基于注解的ControllerAdvice扫描器
 * @author sprout
 * 2022-06-15 11:43
 */
public class AnnotationControllerAdviceHandler implements AnnotationHandler {

    /** ControllerAdvice处理器map */
    private final Map<Class<?>, ControllerAdviceHandler> controllerAdviceHandlerMap = new HashMap<>();

    /**
     * 构建基于注解的ControllerAdvice扫描器
     * @param pluginManager 插件管理器
     */
    public AnnotationControllerAdviceHandler(PluginManager pluginManager) {
        controllerAdviceHandlerMap.put(ExceptionHandler.class, new ExceptionHandlerControllerAdviceHandler(pluginManager));
        controllerAdviceHandlerMap.put(ModelAttribute.class, new ModelAttributeControllerAdviceHandler(pluginManager));
    }

    @Override
    public void handle(Collection<Class<?>> beanClassCollection, Collection<BeanAnnotationScanMetadata> beanMetadataCollection) {
        Collection<ControllerAdviceCombinationMetadata> combinationMetadataCollection = getControllerAdviceCombinationMetadata(beanClassCollection);
        for (ControllerAdviceCombinationMetadata combinationMetadata : combinationMetadataCollection) {
            for (Map.Entry<Class<?>, Collection<ControllerAdviceCombinationMetadataAttributes>> entry : combinationMetadata.getAnnotationMap().entrySet()) {
                ControllerAdviceHandler controllerAdviceHandler = controllerAdviceHandlerMap.get(entry.getKey());
                if (controllerAdviceHandler != null) {
                    for (ControllerAdviceCombinationMetadataAttributes metadataAttributes : entry.getValue()) {
                        controllerAdviceHandler.annotationHandle(combinationMetadata.getBasePackages(), metadataAttributes);
                    }
                }
            }
        }

        // 完成执行
        for (ControllerAdviceHandler adviceHandler : controllerAdviceHandlerMap.values()) {
            adviceHandler.completeHandle();
        }
    }

    /**
     * 获取ControllerAdvice元数据集合
     * @return ControllerAdvice元数据集合
     */
    private Collection<ControllerAdviceCombinationMetadata> getControllerAdviceCombinationMetadata(Collection<Class<?>> collection) {
        Collection<ControllerAdviceCombinationMetadata> combinationMetadataCollection = new HashSet<>();
        for (Class<?> clazz : collection) {
            String[] basePackages = new String[0];
            String[] packages = null;
            if (clazz.isAnnotationPresent(RestControllerAdvice.class)) {
                packages = clazz.getAnnotation(RestControllerAdvice.class).basePackages();
            } else if (clazz.isAssignableFrom(ControllerAdvice.class)) {
                packages = clazz.getAnnotation(ControllerAdvice.class).basePackages();
            }
            if (packages != null) {
                basePackages = packages;
            }

            ControllerAdviceCombinationMetadata combinationMetadata = new ControllerAdviceCombinationMetadata();
            combinationMetadata.setBasePackages(basePackages);
            // 处理具体的拦截方法
            for (Method method : clazz.getMethods()) {
                if (!method.isAnnotationPresent(ExceptionHandler.class) && !method.isAnnotationPresent(ModelAttribute.class)) {
                    continue;
                }

                ExceptionHandler exceptionHandler = method.getAnnotation(ExceptionHandler.class);
                if (exceptionHandler != null) {
                    appendControllerAdviceCombinationMetadataAttributes(clazz, method, exceptionHandler,
                            ExceptionHandler.class, combinationMetadata);
                    continue;
                }

                ModelAttribute modelAttribute = method.getAnnotation(ModelAttribute.class);
                if (modelAttribute != null) {
                    appendControllerAdviceCombinationMetadataAttributes(clazz, method, modelAttribute,
                            ModelAttribute.class, combinationMetadata);
                }
            }
            combinationMetadataCollection.add(combinationMetadata);
        }
        return combinationMetadataCollection;
    }

    /**
     * 追加ControllerAdvice下注解的组合元数据
     * @param clazz ControllerAdvice类
     * @param method 注解方法
     * @param annotation 注解对象
     * @param annotationClass 注解类对象
     * @param combinationMetadata 组合元数据对象
     */
    private void appendControllerAdviceCombinationMetadataAttributes(Class<?> clazz,
                Method method, Annotation annotation, Class<? extends Annotation> annotationClass,
                ControllerAdviceCombinationMetadata combinationMetadata) {
        ControllerAdviceCombinationMetadataAttributes metadataAttributes = new ControllerAdviceCombinationMetadataAttributes();
        metadataAttributes.setType(clazz);
        metadataAttributes.setMethod(method);
        metadataAttributes.setAnnotation(annotation);
        if (clazz.isAnnotationPresent(RestControllerAdvice.class)) {
            metadataAttributes.setResponseBody(true);
        } else {
            metadataAttributes.setResponseBody(method.isAnnotationPresent(ResponseBody.class));
        }
        Collection<ControllerAdviceCombinationMetadataAttributes> attributes =
                combinationMetadata.getAnnotationMap().computeIfAbsent(annotationClass, k -> new ArrayList<>());
        attributes.add(metadataAttributes);
    }
}
